package com.jinly.ryder.account.service.impl;

import com.alibaba.fastjson.JSON;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiDepartmentListRequest;
import com.dingtalk.api.request.OapiGettokenRequest;
import com.dingtalk.api.request.OapiUserGetRequest;
import com.dingtalk.api.request.OapiUserListbypageRequest;
import com.dingtalk.api.response.OapiDepartmentListResponse;
import com.dingtalk.api.response.OapiGettokenResponse;
import com.dingtalk.api.response.OapiUserGetResponse;
import com.dingtalk.api.response.OapiUserListbypageResponse;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.jinly.ryder.account.service.DingtalkService;
import com.jinly.ryder.account.service.model.DingTalkDept;
import com.jinly.ryder.account.service.model.DingTalkUserInfo;
import com.jinly.ryder.common.error.SystemErrorType;
import com.jinly.ryder.common.service.SystemSettingService;
import com.jinly.ryder.common.service.model.setting.DingTalkSetting;
import com.taobao.api.ApiException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@RequiredArgsConstructor
public class DingtalkServiceImpl implements DingtalkService {
    private final static String ACCESS_TOKEN_KEY = "ding_access_token";

    private final StringRedisTemplate redisTemplate;
    private final SystemSettingService systemSettingService;

    @Override
    public List<DingTalkDept> getDeptList() {
        List<DingTalkDept> deptList = new ArrayList<>();
        DingTalkSetting dingTalkSetting = systemSettingService.getSettingItem(DingTalkSetting.class);

        String accessToken = getAccessToken(dingTalkSetting.getMiniAppKey(), dingTalkSetting.getMiniAppSecret());
        try {
            DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/department/list");
            OapiDepartmentListRequest request = new OapiDepartmentListRequest();
            request.setFetchChild(true);
            request.setHttpMethod("GET");
            OapiDepartmentListResponse response = client.execute(request, accessToken);
            if (response.isSuccess()) {
                log.debug("[钉钉API]获取部门列表返回：{}", response.getBody());
                List<OapiDepartmentListResponse.Department> list = response.getDepartment();
                for (OapiDepartmentListResponse.Department department : list) {
                    DingTalkDept dingTalkDept = new DingTalkDept();
                    dingTalkDept.setDeptId(department.getId());
                    dingTalkDept.setParentId(department.getParentid());
                    dingTalkDept.setName(department.getName());
                    deptList.add(dingTalkDept);
                }
            } else {
                log.error("[钉钉API]获取部门列表失败：{}-{}", response.getErrcode(), response.getErrmsg());
                SystemErrorType.INTERNAL_SERVER_ERROR.throwError();
            }
        } catch (ApiException e) {
            log.error("[钉钉API]获取部门列表失败：{}-{}", e.getErrCode(), e.getErrMsg());
            SystemErrorType.INTERNAL_SERVER_ERROR.throwError();
        }
        return deptList;
    }

    @Override
    public List<DingTalkUserInfo> getUserListByDeptId(Long deptId) {
        List<DingTalkUserInfo> list = new ArrayList<>();
        DingTalkSetting dingTalkSetting = systemSettingService.getSettingItem(DingTalkSetting.class);
        String accessToken = getAccessToken(dingTalkSetting.getMiniAppKey(), dingTalkSetting.getMiniAppSecret());
        boolean hasNext = true;
        long offset = 0;
        long size = 50;
        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/user/listbypage");
        while (hasNext) {
            try {
                OapiUserListbypageRequest request = new OapiUserListbypageRequest();
                request.setDepartmentId(deptId);
                request.setOffset(offset);
                request.setSize(size);
                request.setOrder("entry_desc");
                request.setHttpMethod("GET");
                OapiUserListbypageResponse response = client.execute(request, accessToken);
                if (response.isSuccess()) {
                    log.debug("[钉钉API]根据deptId[{}]获取部门用户列表：{}", deptId, response.getBody());
                    List<OapiUserListbypageResponse.Userlist> userlist = response.getUserlist();
                    if (userlist.size() < size) {
                        hasNext = false;
                    } else {
                        offset += size;
                    }
                    userlist.forEach(u -> {
                        DingTalkUserInfo dingTalkUserInfo = new DingTalkUserInfo();
                        BeanUtils.copyProperties(u, dingTalkUserInfo);
                        dingTalkUserInfo.setEmail(u.getOrgEmail());
                        dingTalkUserInfo.setDepartments(JSON.parseArray(u.getDepartment(), Long.class));
                        list.add(dingTalkUserInfo);
                    });
                } else {
                    log.error("[钉钉API]根据deptId[{}]获取部门用户列表失败：{}-{}", deptId, response.getErrcode(), response.getErrmsg());
                    SystemErrorType.INTERNAL_SERVER_ERROR.throwError();
                }
            } catch (ApiException e) {
                log.error("[钉钉API]根据deptId[{}]获取部门用户列表失败：{}-{}", deptId, e.getErrCode(), e.getErrMsg());
                SystemErrorType.INTERNAL_SERVER_ERROR.throwError();
            }
        }
        return list;
    }

    @Override
    public DingTalkUserInfo getUserInfo(String userId) {
        log.info("[钉钉API]开始 根据userId {}获取用户信息", userId);
        DingTalkSetting dingTalkSetting = systemSettingService.getSettingItem(DingTalkSetting.class);
        String accessToken = getAccessToken(dingTalkSetting.getMiniAppKey(), dingTalkSetting.getMiniAppSecret());
        DingTalkUserInfo userInfo = null;
        try {
            DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/user/get");
            OapiUserGetRequest request = new OapiUserGetRequest();
            request.setUserid(userId);
            request.setHttpMethod("GET");

            OapiUserGetResponse response = client.execute(request, accessToken);
            if (response.isSuccess()) {
                userInfo = new DingTalkUserInfo();
                BeanUtils.copyProperties(response, userInfo);
                log.info("[钉钉API]开始 根据userId {}获取用户 unionId {}", userId, userInfo.getUnionid());
            } else {
                log.error("[钉钉API]根据userId {} 获取用户信息失败：{}-{}", userId, response.getErrcode(), response.getErrmsg());
                SystemErrorType.INTERNAL_SERVER_ERROR.throwError();
            }

        } catch (ApiException error) {
            log.error("[钉钉API]根据userId {} 获取用户信息失败 {}-{}", userId, error.getErrCode(), error.getErrMsg());
            SystemErrorType.INTERNAL_SERVER_ERROR.throwError();
        }
        return userInfo;
    }

    @Override
    public String getAccessToken(String appKey, String appSecret) {
        String cacheKey = Joiner.on(":").join(ACCESS_TOKEN_KEY, appKey);
        String accessToken = redisTemplate.opsForValue().get(cacheKey);
        if (Strings.isNullOrEmpty(accessToken)) {
            try {
                DefaultDingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/gettoken");
                OapiGettokenRequest request = new OapiGettokenRequest();
                request.setAppkey(appKey);
                request.setAppsecret(appSecret);
                request.setHttpMethod("GET");
                OapiGettokenResponse response = client.execute(request);
                if (response.isSuccess()) {
                    log.info("[钉钉API]获取accessToken返回：{}", response.getBody());
                    accessToken = response.getAccessToken();
                    redisTemplate.opsForValue().set(cacheKey, accessToken, 7100, TimeUnit.SECONDS);
                } else {
                    log.error("[钉钉API]获取accessToken失败：{}-{}", response.getErrcode(), response.getErrmsg());
                    SystemErrorType.INTERNAL_SERVER_ERROR.throwError();
                }
            } catch (ApiException e) {
                log.error("[钉钉API]获取accessToken失败：{}-{}", e.getErrCode(), e.getErrMsg());
                SystemErrorType.INTERNAL_SERVER_ERROR.throwError();
            }
        }
        return accessToken;
    }
}
